
`include "defines.v"
`include "aluop_defines.v"

module mem(
 
    input  wire               rst,

    input  wire               rd_w_ena_i,
    input  wire [`REG_BUS]    rd_w_addr_i,
    input  wire [`REG_WIDTH]  rd_w_data_i,
    
    input  wire [5:0]         aluop_i,
    input  wire [`RAM_BUS]    mem_addr_i,
    input  wire [`REG_WIDTH]  rs2_i, 
    input  wire               halt_ena_i,
    input  wire               skip_exe_i,

    //csr
    input  wire [`REG_WIDTH]  mtime_i, 
    input  wire [`REG_WIDTH]  mtimecmp_i, 
    output wire               clint_rd_o,
    output wire               clint_wr_o,
    output wire               mtime_ena_o,
    output wire               mtimecmp_ena_o,         

    //axi  
    output wire               mem_read_req_o,   
    output wire               mem_write_req_o,        
    output wire               mem_valid_o,
    input  wire               mem_ready_i,
    output wire [`RAM_BUS]    mem_addr_o,
    output wire [1:0]         mem_size_o,
    input  wire [`RAM_WIDTH]  mem_data_read_i,
    output wire [`RAM_WIDTH]  mem_data_write_o, 
  
    //wb_stage    
    output reg                rd_w_ena_o,
    output reg [`REG_BUS]     rd_w_addr_o,
    output reg [`REG_WIDTH]   rd_w_data_o,
    output wire               halt_ena_o,
    output wire               skip_mem_o,
    output wire               mem_data_valid_o




    );
    //test
    wire rw_b = mem_size == `SIZE_B && mem_write_req_o && mem_data_write_o != 0;
    wire rw_h = mem_size == `SIZE_H && mem_write_req_o && mem_data_write_o != 0;
    wire rw_w = mem_size == `SIZE_W && mem_write_req_o && mem_data_write_o != 0;
    wire rw_d = mem_size == `SIZE_D && mem_write_req_o && mem_data_write_o != 0;
    
    wire load_inst;
    wire store_inst;
    wire clint_inst;
    wire mem_hs;
    wire [`RAM_WIDTH] mem_data_read;
    reg [1:0] mem_size;
    reg [`RAM_BUS] mem_addr;
    reg [`RAM_WIDTH] mem_data_write;

    assign load_inst = ((aluop_i == `ALUOP_LB) || (aluop_i == `ALUOP_LH) || (aluop_i == `ALUOP_LW) || (aluop_i == `ALUOP_LD)
            || (aluop_i == `ALUOP_LBU) || (aluop_i == `ALUOP_LHU) || (aluop_i == `ALUOP_LWU)) ? 1'b1 : 1'b0;
    assign store_inst = ((aluop_i == `ALUOP_SB) || (aluop_i == `ALUOP_SH) || (aluop_i == `ALUOP_SW) || (aluop_i == `ALUOP_SD)) ? 1'b1 : 1'b0;
    assign mem_hs = mem_valid_o && mem_ready_i;

    assign halt_ena_o = halt_ena_i;
    assign skip_mem_o = skip_exe_i;
    assign mem_data_valid_o = mem_valid_o & mem_ready_i;
    assign clint_inst = (mem_addr_i == `MTIMECMP_ADDR | mem_addr_i == `MTIME_ADDR);
    assign mtime_ena_o = mem_addr_i == `MTIME_ADDR;
    assign mtimecmp_ena_o = mem_addr_i == `MTIMECMP_ADDR;
    assign clint_rd_o = clint_inst & load_inst;
    assign clint_wr_o = clint_inst & store_inst;
    assign mem_data_read = ~clint_rd_o? mem_data_read_i:
                           (mem_addr_i == `MTIMECMP_ADDR)? mtimecmp_i:
                           mtime_i;

    //axi
    assign mem_read_req_o   = load_inst;
    assign mem_write_req_o  = store_inst;
    assign mem_valid_o      = (load_inst || store_inst) & ~clint_inst ;
    assign mem_addr_o       = mem_addr;
    assign mem_size_o       = mem_size;
    assign mem_data_write_o = mem_data_write;
    
    parameter [0:0] MEM_IDLE  = 1'b0;
    parameter [0:0] MEM_VALID = 1'b1;




    always @ (*) begin
        if(rst == `RST) begin
            rd_w_ena_o     =  1'b0;
            rd_w_addr_o    =  `REG_ADDR_0;
            rd_w_data_o    =  `ZERO_WORD;
            mem_addr       =  `ZERO_WORD;
            mem_data_write =  `ZERO_WORD;
            mem_size       =  2'b0; 
        end
        else begin
            if(load_inst == 1'b1) begin
                rd_w_ena_o     = rd_w_ena_i;           
                rd_w_addr_o    = rd_w_addr_i;
                mem_data_write = `ZERO_WORD;
                mem_addr       = mem_addr_i;
                case(aluop_i)
                    `ALUOP_LB  : begin mem_size = `SIZE_B; rd_w_data_o = {{ 56{mem_data_read[7]} },mem_data_read[7:0]};    end
                    `ALUOP_LH  : begin mem_size = `SIZE_H; rd_w_data_o = {{ 48{mem_data_read[15]} },mem_data_read[15:0]};  end
                    `ALUOP_LW  : begin mem_size = `SIZE_W; rd_w_data_o = {{ 32{mem_data_read[31]} },mem_data_read[31:0]};  end
                    `ALUOP_LD  : begin mem_size = `SIZE_D; rd_w_data_o = mem_data_read;                                      end
                    `ALUOP_LBU : begin mem_size = `SIZE_B; rd_w_data_o = {56'b0,mem_data_read[7:0]};                         end
                    `ALUOP_LHU : begin mem_size = `SIZE_H; rd_w_data_o = {48'b0,mem_data_read[15:0]};                        end
                    `ALUOP_LWU : begin mem_size = `SIZE_W; rd_w_data_o = {32'b0,mem_data_read[32:0]};                        end 
                    default    : begin mem_size =  2'b0;   rd_w_data_o = rd_w_data_i;                                          end                                          
                endcase
            end
            else if(store_inst == 1'b1)begin
                rd_w_ena_o   = 1'b0;
                rd_w_addr_o  = `REG_ADDR_0;
                rd_w_data_o  = `ZERO_WORD;
                mem_addr     = mem_addr_i;
                case(aluop_i)
                    `ALUOP_SB : begin mem_size = `SIZE_B; mem_data_write = { 56'b0, rs2_i[7:0] };  end
                    `ALUOP_SH : begin mem_size = `SIZE_H; mem_data_write = { 48'b0, rs2_i[15:0] }; end  
                    `ALUOP_SW : begin mem_size = `SIZE_W; mem_data_write = { 32'b0, rs2_i[31:0] }; end
                    `ALUOP_SD : begin mem_size = `SIZE_D; mem_data_write = rs2_i;                  end
                    default   : begin mem_size = 2'b0;    mem_data_write = `ZERO_WORD;             end  
                endcase
            end
            else begin
                rd_w_ena_o     = rd_w_ena_i;
                rd_w_addr_o    = rd_w_addr_i;
                rd_w_data_o    = rd_w_data_i;
                mem_addr       = `ZERO_WORD;
                mem_data_write = `ZERO_WORD;
                mem_size       = 2'b0; 
            end
        end
    end
   
endmodule
